package de.mrapp.tabswitcher;

import de.mrapp.materialview.library.utils.AttrUtils;
import de.mrapp.tabswitcher.gesture.AbstractTouchEventHandler;
import de.mrapp.tabswitcher.gesture.DragGestureEventHandlerFactory;
import de.mrapp.tabswitcher.gesture.TouchEventDispatcher;
import de.mrapp.tabswitcher.layout.AbstractTabSwitcherLayout;
import de.mrapp.tabswitcher.layout.ContentRecyclerAdapter;
import de.mrapp.tabswitcher.layout.TabSwitcherLayout;
import de.mrapp.tabswitcher.layout.phone.PhoneArithmetics;
import de.mrapp.tabswitcher.layout.phone.PhoneTabSwitcherLayout;
import de.mrapp.tabswitcher.menu.OnMenuItemClickListener;
import de.mrapp.tabswitcher.model.Model;
import de.mrapp.tabswitcher.model.TabSwitcherModel;
import de.mrapp.tabswitcher.model.TabSwitcherStyle;
import de.mrapp.tabswitcher.util.LogUtil;
import de.mrapp.tabswitcher.util.ThemeHelper;
import de.mrapp.tabswitcher.view.Menu;
import de.mrapp.tabswitcher.view.TabSwitcherButton;
import de.mrapp.tabswitcher.view.Toolbar;
import de.mrapp.util.Condition;
import de.mrapp.ohos_util.DisplayUtil;
import de.mrapp.ohos_util.ViewUtil;
import de.mrapp.ohos_util.logging.LogLevel;
import de.mrapp.ohos_util.view.AbstractSavedState;
import de.mrapp.ohos_util.view.AbstractViewRecycler;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Canvas;
import ohos.agp.utils.Color;
import ohos.agp.utils.TextTool;
import ohos.app.Context;
import ohos.global.resource.solidxml.TypedAttribute;
import ohos.media.image.PixelMap;
import ohos.multimodalinput.event.TouchEvent;
import ohos.utils.PacMap;
import ohos.utils.Parcel;
import ohos.utils.Sequenceable;

import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;

import static de.mrapp.ohos_util.DisplayUtil.getOrientation;

public class TabSwitcher extends StackLayout implements TabSwitcherLayout, Model, Component.TouchEventListener {

    /**
     * A saved state, which allows to store the state of a {@link TabSwitcher}.
     */
    private static class TabSwitcherState extends AbstractSavedState {

        /**
         * A creator, which allows to create instances of the class {@link TabSwitcherState}.
         */
        public static Producer<TabSwitcherState> CREATOR = new Producer<TabSwitcherState>() {

            @Override
            public TabSwitcherState createFromParcel(final Parcel source) {
                return new TabSwitcherState(source);
            }

        };

        /**
         * The saved layout policy, which is used by the tab switcher.
         */
        private LayoutPolicy layoutPolicy;

        /**
         * The saved state of the model, which is used by the tab switcher.
         */
        private PacMap modelState;

        /**
         * Creates a new saved state, which allows to store the state of a {@link TabSwitcher}.
         *
         * @param source The parcel to read read from as a instance of the class {@link Parcel}. The
         *               parcel may not be null
         */
        private TabSwitcherState(final Parcel source) {
            super(source);
            layoutPolicy = source.readSerializable(LayoutPolicy.class);
            modelState = (PacMap) source.readParcelableEx(getClass().getClassLoader());
        }

        /**
         * Creates a new saved state, which allows to store the state of a {@link TabSwitcher}.
         *
         * @param superState The state of the superclass of the view, this saved state corresponds to, as an
         *                   instance of the type {@link Sequenceable} or null if no state is available
         */
        TabSwitcherState(final Sequenceable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(Parcel dest) {
            super.writeToParcel(dest);
            dest.writeSerializable(layoutPolicy);
            dest.writeSequenceable(modelState);
        }

    }

    /**
     * The index of the primary toolbar as returned by the method {@link
     * TabSwitcher#getToolbars()}.
     */
    public static final int PRIMARY_TOOLBAR_INDEX = 0;

    /**
     * The index of the secondary toolbar as returned by the method {@link
     * TabSwitcher#getToolbars()}.
     */
    public static final int SECONDARY_TOOLBAR_INDEX = 1;

    /**
     * A queue, which contains all pending actions.
     */
    private Queue<Runnable> pendingActions;

    /**
     * A set, which contains the listeners, which should be notified about the tab switcher's
     * events.
     */
    private Set<TabSwitcherListener> listeners;

    /**
     * The layout policy, which is used by the tab switcher.
     */
    private LayoutPolicy layoutPolicy;

    /**
     * The model, which is used by the tab switcher.
     */
    private TabSwitcherModel model;

    /**
     * The style, which allows to retrieve the style attributes of the tab switcher.
     */
    private TabSwitcherStyle style;

    /**
     * The theme helper, which allows to retrieve resources, depending on the tab switcher's theme.
     */
    private ThemeHelper themeHelper;

    /**
     * The dispatcher, which is used to dispatch touch events.
     */
    private TouchEventDispatcher touchEventDispatcher;

    /**
     * The layout, which is used by the tab switcher, depending on whether the device is a
     * smartphone or tablet and the set layout policy.
     */
    private AbstractTabSwitcherLayout layout;

    /**
     * Whether to preserve the tab switcher's state, or not.
     */
    private boolean preserveState = true;

    /**
     * Initializes the view.
     *
     * @param attrSet              The attribute set, which should be used to initialize the view, as an instance of the
     *                             type {@link AttrSet} or null, if no attributes should be obtained
     * @param defaultStyle         The default style to apply to this view. If 0, no style will be applied (beyond what
     *                             is included in the theme). This may either be an attribute resource, whose value will
     *                             be retrieved from the current theme, or an explicit style resource
     * @param defaultStyleResource A resource identifier of a style resource that supplies default values for the view,
     *                             used only if the default style is 0 or can not be found in the theme. Can be 0 to not
     *                             look for defaults
     */
    private void initialize(final AttrSet attrSet, final String defaultStyle, final int defaultStyleResource) {
        pendingActions = new LinkedList<>();
        listeners = new CopyOnWriteArraySet<>();
        model = new TabSwitcherModel(this);
        model.addListener(createModelListener());
        setTouchEventListener(this::onTouchEvent);
        touchEventDispatcher = new TouchEventDispatcher();
        setPadding(super.getPaddingLeft(), super.getPaddingTop(), super.getPaddingRight(), super.getPaddingBottom());
        obtainStyledAttributes(attrSet, defaultStyle, defaultStyleResource);
        getComponentTreeObserver().addWindowBoundListener(
                new AbstractTabSwitcherLayout.LayoutListenerWrapper(this, createGlobalLayoutListener(false)));

    }

    /**
     * Initializes a specific layout.
     *
     * @param inflatedTabsOnly True, if only the tabs should be inflated, false otherwise
     * @param layout           The layout, which should be initialized, as a value of the enum {@link Layout}. The
     *                         layout may not be null
     */
    private void initializeLayout(final Layout layout, final boolean inflatedTabsOnly) {
//        if (layout == Layout.TABLET) {
            this.layout = new PhoneTabSwitcherLayout(TabSwitcher.this, model,
                    new PhoneArithmetics(TabSwitcher.this), style, touchEventDispatcher);
//        } else {
//            this.layout = new PhoneTabSwitcherLayout(TabSwitcher.this, model,
//                    new PhoneArithmetics(TabSwitcher.this), style, touchEventDispatcher);
//        }

        this.layout.setCallback(createLayoutCallback());
        this.model.addListener(this.layout);
        this.layout.inflateLayout(inflatedTabsOnly);
        this.touchEventDispatcher.addEventHandler(this.layout.getDragHandler());
        final ComponentContainer tabContainer = getTabContainer();
        assert tabContainer != null;

        if (tabContainer.getWidth() > 0 && tabContainer.getHeight() > 0) {
            this.layout.onWindowBound();
        } else {
            tabContainer.getComponentTreeObserver().addWindowBoundListener(
                    new ComponentTreeObserver.WindowBoundListener() {

                        @Override
                        public void onWindowBound() {
                            TabSwitcher.this.layout.onWindowBound();
                        }

                        @Override
                        public void onWindowUnbound() {
                            ViewUtil.removeOnGlobalLayoutListener(tabContainer.getComponentTreeObserver(), this);
                        }

                    });
        }
    }

    /**
     * Obtains all attributes from a specific attribute set.
     *
     * @param attrSet              The attribute set, the attributes should be obtained from, as an instance of the type
     *                             {@link AttrSet} or null, if no attributes should be obtained
     * @param defaultStyle         The default style to apply to this view. If 0, no style will be applied (beyond what
     *                             is included in the theme). This may either be an attribute resource, whose value will
     *                             be retrieved from the current theme, or an explicit style resource
     * @param defaultStyleResource A resource identifier of a style resource that supplies default values for the view,
     *                             used only if the default style is 0 or can not be found in the theme. Can be 0 to not
     *                             look for defaults
     */
    private void obtainStyledAttributes(final AttrSet attrSet, final String defaultStyle, final int defaultStyleResource) {
        //主题相关
        int globalTheme = AttrUtils.getIntegerFromAttr(attrSet, "themeGlobal", 0);
        int phoneTheme = AttrUtils.getIntegerFromAttr(attrSet, "themePhone", 0);
        int tabletTheme = AttrUtils.getIntegerFromAttr(attrSet, "themeTablet", 0);
        themeHelper = new ThemeHelper(getContext(), globalTheme, phoneTheme, tabletTheme);
        style = new TabSwitcherStyle(this, model, themeHelper);
        //保存状态
        preserveState = AttrUtils.getBooleanFromAttr(attrSet, "preserveState", false);
        //设备布局状态 auto:0  phone:1  tablet:2  tablet_landscape:3
        int value = AttrUtils.getIntegerFromAttr(attrSet, "layoutPolicy", 0);
//        if (value == 0) {
//            value = themeHelper.getInteger(getLayout(), ResourceTable.attr.tabSwitcherLayoutPolicy, 0);
//        }
        setLayoutPolicy(LayoutPolicy.fromValue(value));
        obtainBackground(attrSet);
        obtainTabIcon(attrSet);
        obtainTabBackgroundColor(attrSet);
//        obtainTabContentBackgroundColor(typedArray);
//        obtainAddTabButtonColor(typedArray);
//        obtainTabTitleTextColor(typedArray);
//        obtainTabCloseButtonIcon(typedArray);
//        obtainTabCloseButtonIconTint(typedArray);
//        obtainShowToolbars(typedArray);
//        obtainToolbarTitle(typedArray);
//        obtainToolbarNavigationIcon(typedArray);
//        obtainToolbarNavigationIconTint(typedArray);
//        obtainToolbarMenu(typedArray);
//        obtainTabPreviewFadeThreshold(typedArray);
//        obtainTabPreviewFadeDuration(typedArray);
        obtainEmptyView(attrSet);
//            } finally {
//                typedArray = null;
//            }
//        }
    }

    private void obtainBackground(AttrSet attrSet) {

        int bgColor = AttrUtils.getColorFromAttr(attrSet, "ohos_background", 0);
        if (bgColor == 0) {
            try {
                String bgColorStr = getResourceManager().getElement(ResourceTable.Color_phone_tab_switcher_background_color).getString();
                int color = Color.getIntColor(bgColorStr);
                RgbColor rgbColor = RgbColor.fromArgbInt(color);
                ShapeElement background = new ShapeElement();
                background.setRgbColor(rgbColor);
                ViewUtil.setBackground(this, background);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    private void obtainTabIcon(final AttrSet attrSet) {
        String tabIconStr = AttrUtils.getStringFromAttr(attrSet, "tabIcon", "");
        if(!tabIconStr.contains("$media:"))
            return;
        String tabIconSrc = tabIconStr.replace("$media:", "");
        int resourceId = 0;
        try {
            resourceId = Integer.parseInt(tabIconSrc);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (resourceId != 0) {
            setTabIcon(resourceId);
        }
    }

    private void obtainTabBackgroundColor(final AttrSet attrSet) {
//        setTabBackgroundColor(typedArray.getColorValue(ResourceTable.styleable.TabSwitcher_tabBackgroundColor));
    }


    private void obtainTabContentBackgroundColor(final TypedAttribute typedArray) {
//        setTabContentBackgroundColor(
//                typedArray.getColorValue(ResourceTable.styleable.TabSwitcher_tabContentBackgroundColor, -1));
    }


    private void obtainAddTabButtonColor(final TypedAttribute typedArray) {
//        setAddTabButtonColor(
//                typedArray.getColorValue(ResourceTable.styleable.TabSwitcher_addTabButtonColor));
    }


    private void obtainTabTitleTextColor(final TypedAttribute typedArray) {
//        setTabTitleTextColor(
//                typedArray.getColorValue(ResourceTable.styleable.TabSwitcher_tabTitleTextColor));
    }

    private void obtainTabCloseButtonIcon(final TypedAttribute typedArray) {
//        int resourceId = typedArray.getResId(ResourceTable.styleable.TabSwitcher_tabCloseButtonIcon, 0);
//
//        if (resourceId != 0) {
//            setTabCloseButtonIcon(resourceId);
//        }
    }


    private void obtainTabCloseButtonIconTint(final TypedAttribute typedArray) {
//        setTabIconTintList(
//                typedArray.getColorValue(ResourceTable.styleable.TabSwitcher_tabCloseButtonIconTint));
    }

    /**
     * Obtains, whether the tab switcher's toolbars should be shown, or not, from a specific typed
     * array.
     *
     * @param typedArray The typed array, it should be obtained from, whether the toolbars should be shown, as
     *                   an instance of the class {@link TypedAttribute}. The typed array may not be null
     */
    private void obtainShowToolbars(final TypedAttribute typedArray) {
//        showToolbars(typedArray.getBooleanValue(ResourceTable.styleable.TabSwitcher_showToolbars, false));
    }

    /**
     * Obtains the title of the toolbar, which is shown, when the tab switcher is shown, from a
     * specific typed array.
     *
     * @param typedArray The typed array, the title should be obtained from, as an instance of the class
     *                   {@link TypedAttribute}. The typed array may not be null
     */
    private void obtainToolbarTitle(final TypedAttribute typedArray) {
//        setToolbarTitle(typedArray.getText(ResourceTable.styleable.TabSwitcher_toolbarTitle));
    }

    /**
     * Obtains the navigation icon of the toolbar, which is shown, when the tab switcher is shown,
     * from a specific typed array.
     *
     * @param typedArray The typed array, the navigation icon should be obtained from, as an instance of the
     *                   class {@link TypedAttribute}. The typed array may not be null
     */
    private void obtainToolbarNavigationIcon(final TypedAttribute typedArray) {
//        int resourceId =
//                typedArray.getResId(ResourceTable.styleable.TabSwitcher_toolbarNavigationIcon, -1);
//        Drawable navigationIcon = null;
//
//        if (resourceId != -1) {
//            navigationIcon = AppCompatResources.getDrawable(getContext(), resourceId);
//        }
//
//        setToolbarNavigationIcon(navigationIcon, null);
    }

    /**
     * Obtains the color state list, which should be used to tint the navigation icon of the
     * toolbar, which is shown, when the tab switcher is shown, from a specific typed array.
     *
     * @param typedArray The typed array, the color state list should be obtained from, as an instance of the
     *                   class {@link TypedAttribute}. The typed array may not be null
     */
    private void obtainToolbarNavigationIconTint(final TypedAttribute typedArray) {
//        setToolbarNavigationIconTintList(
//                typedArray.getColorValue(ResourceTable.styleable.TabSwitcher_toolbarNavigationIconTint));
    }

    /**
     * Obtains the menu of the toolbar, which is shown, when the tab switcher is shown, from a
     * specific typed array.
     *
     * @param typedArray The typed array, the menu should be obtained from, as an instance of the class {@link
     *                   TypedAttribute}. The typed array may not be null
     */
    private void obtainToolbarMenu(final TypedAttribute typedArray) {
//        int resourceId = typedArray.getResId(ResourceTable.styleable.TabSwitcher_toolbarMenu, 0);
//
//        if (resourceId == 0) {
//            resourceId = themeHelper.getResourceId(getLayout(), ResourceTable.attr.tabSwitcherToolbarMenu, 0);
//        }
//
//        if (resourceId != 0) {
//            inflateToolbarMenu(resourceId, null);
//        }
    }

    /**
     * Obtains the duration, which must be reached when loading the preview of tabs to use a fade
     * duration, from a specific typed array.
     *
     * @param typedArray The typed array, the duration should be obtained from, as an instance of the class
     *                   {@link TypedAttribute}. The typed array may not be null
     */
    private void obtainTabPreviewFadeThreshold(final TypedAttribute typedArray) {
//        int threshold = typedArray.getColorValue(ResourceTable.styleable.TabSwitcher_tabPreviewFadeThreshold, -1);
//
//        if (threshold == -1) {
//            threshold = themeHelper
//                    .getInteger(getLayout(), ResourceTable.attr.tabSwitcherTabToolbarPreviewFadeThreshold, -1);
//        }
//
//        if (threshold != -1) {
//            setTabPreviewFadeThreshold(threshold);
//        }
    }

    /**
     * Sets the duration of the fade animation, which is used to show the previews of tabs.
     *
     * @param typedArray The typed array, the duration should be obtained from, as an instance of the class
     *                   {@link TypedAttribute}. The typed array may not be null
     */
    private void obtainTabPreviewFadeDuration(final TypedAttribute typedArray) {
//        int duration = typedArray.getIntegerValue(ResourceTable.styleable.TabSwitcher_tabPreviewFadeDuration, -1);
//
//        if (duration == -1) {
//            duration = themeHelper
//                    .getInteger(getLayout(), ResourceTable.attr.tabSwitcherTabToolbarPreviewFadeDuration, -1);
//        }
//
//        if (duration != -1) {
//            setTabPreviewFadeDuration(duration);
//        }
    }


    private void obtainEmptyView(final AttrSet attrSet) {
        String resourceStr = AttrUtils.getStringFromAttr(attrSet, "emptyView", "");
        if (TextTool.isNullOrEmpty(resourceStr) || !resourceStr.contains("$layout:"))
            return;
        int resourceId = Integer.parseInt(resourceStr.replace("$layout:", ""));

        if (resourceId != 0) {
            long animationDuration = convertIntegerValue(getContext(), ResourceTable.Integer_empty_view_animation_duration);
//
//            if (animationDuration < -1) {
//                animationDuration = themeHelper
//                        .getInteger(getLayout(), ResourceTable.attr.tabSwitcherEmptyViewAnimationDuration, -1);
//            }
//
            setEmptyView(resourceId, animationDuration);
        }
    }

    private int convertIntegerValue(Context context, int id) {
        int value = 0;
        try {
            value = context.getResourceManager().getElement(id).getInteger();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;
    }

    /**
     * Enqueues a specific action to be executed, when no animation is running.
     *
     * @param action The action, which should be enqueued as an instance of the type {@link Runnable}. The
     *               action may not be null
     */
    private void enqueuePendingAction(final Runnable action) {
        Condition.INSTANCE.ensureNotNull(action, "The action may not be null");
        pendingActions.add(action);
        executePendingAction();
    }

    /**
     * Executes the next pending action.
     */
    private void executePendingAction() {
        LogUtil.loge("=== executePendingAction AnimationRunning " + isAnimationRunning() + " pendingActions size: " + pendingActions.size());
        if (!isAnimationRunning()) {
            final Runnable action = pendingActions.poll();

            if (action != null) {
                ((Runnable) () -> {
                    action.run();
                    executePendingAction();
                }).run();
            }
        }
    }

    public AbstractTabSwitcherLayout getTabSwitcherLayout() {
        return layout;
    }

    /**
     * Creates and returns a listener, which allows to observe, when the tab switcher's model is
     * modified.
     *
     * @return The listener, which has been created, as an instance of the type {@link
     * Model.Listener}. The listener may not be null
     */

    private Model.Listener createModelListener() {
        return new Model.Listener() {

            @Override
            public void onLogLevelChanged(final LogLevel logLevel) {

            }

            @Override
            public void onDecoratorChanged(final TabSwitcherDecorator decorator) {

            }

            @Override
            public void onSwitcherShown() {
                notifyOnSwitcherShown();
            }

            @Override
            public void onSwitcherHidden() {
                notifyOnSwitcherHidden();
            }

            @Override
            public void onSelectionChanged(final int previousIndex, final int index,
                                           final Tab selectedTab,
                                           final boolean switcherHidden) {
                notifyOnSelectionChanged(index, selectedTab);

                if (switcherHidden) {
                    notifyOnSwitcherHidden();
                }
            }

            @Override
            public void onTabAdded(final int index, final Tab tab,
                                   final int previousSelectedTabIndex, final int selectedTabIndex,
                                   final boolean selectionChanged,
                                   final boolean switcherVisibilityChanged,
                                   final Animation animation) {
                notifyOnTabAdded(index, tab, animation);

                if (selectionChanged) {
                    notifyOnSelectionChanged(selectedTabIndex,
                            selectedTabIndex != -1 ? getTab(selectedTabIndex) : null);
                }

                if (switcherVisibilityChanged) {
                    notifyOnSwitcherHidden();
                }
            }

            @Override
            public void onAllTabsAdded(final int index, final Tab[] tabs,
                                       final int previousSelectedTabIndex,
                                       final int selectedTabIndex, final boolean selectionChanged,
                                       final Animation animation) {
                for (Tab tab : tabs) {
                    notifyOnTabAdded(index, tab, animation);
                }

                if (selectionChanged) {
                    notifyOnSelectionChanged(selectedTabIndex,
                            selectedTabIndex != -1 ? getTab(selectedTabIndex) : null);
                }
            }

            @Override
            public void onTabRemoved(final int index, final Tab tab,
                                     final int previousSelectedTabIndex, final int selectedTabIndex,
                                     final boolean selectionChanged,
                                     final Animation animation) {
                notifyOnTabRemoved(index, tab, animation);

                if (selectionChanged) {
                    notifyOnSelectionChanged(selectedTabIndex,
                            selectedTabIndex != -1 ? getTab(selectedTabIndex) : null);
                }
            }

            @Override
            public void onAllTabsRemoved(final Tab[] tabs,
                                         final Animation animation) {
                notifyOnAllTabsRemoved(tabs, animation);
                notifyOnSelectionChanged(-1, null);
            }

            @Override
            public void onPaddingChanged(final int left, final int top, final int right,
                                         final int bottom) {

            }

            @Override
            public void onApplyPaddingToTabsChanged(final boolean applyPaddingToTabs) {

            }

            @Override
            public void onTabIconChanged(final Element icon) {

            }

            @Override
            public void onTabBackgroundColorChanged(final Color colorStateList) {

            }

            @Override
            public void onTabContentBackgroundColorChanged(final int color) {

            }

            @Override
            public void onTabTitleColorChanged(final Color colorStateList) {

            }

            @Override
            public void onTabCloseButtonIconChanged(final Element icon) {

            }

            @Override
            public void onTabProgressBarColorChanged(final int color) {

            }

            @Override
            public void onAddTabButtonVisibilityChanged(final boolean visible) {

            }

            @Override
            public void onAddTabButtonColorChanged(final Color colorStateList) {

            }

            @Override
            public void onToolbarVisibilityChanged(final boolean visible) {

            }

            @Override
            public void onToolbarTitleChanged(final CharSequence title) {

            }

            @Override
            public void onToolbarNavigationIconChanged(final Element icon,
                                                       final ClickedListener listener) {

            }

            @Override
            public void onToolbarMenuInflated(final int resourceId,
                                              final OnMenuItemClickListener listener) {

            }

            @Override
            public void onEmptyViewChanged(final Component view,
                                           final long animationDuration) {

            }

        };
    }

    /**
     * Creates and returns a callback, which allows to observe, when all pending animations of a
     * layout have been ended.
     *
     * @return The callback, which has been created, as an instance of the type {@link
     * AbstractTabSwitcherLayout.Callback}. The callback may not be null
     */

    private AbstractTabSwitcherLayout.Callback createLayoutCallback() {
        return new AbstractTabSwitcherLayout.Callback() {

            @Override
            public void onAnimationsEnded() {
                executePendingAction();
            }

        };
    }

    /**
     * Creates and returns a listener, which allows to inflate the view's layout once the view is
     * laid out.
     *
     * @param inflateTabsOnly True, if only the tabs should be inflated, false otherwise
     * @return The listener, which has been created, as an instance of the type {@link
     * ComponentTreeObserver.GlobalLayoutListener}. The listener may not be null
     */

    private ComponentTreeObserver.WindowBoundListener createGlobalLayoutListener(final boolean inflateTabsOnly) {
        return new ComponentTreeObserver.WindowBoundListener() {

            @Override
            public void onWindowBound() {
                Condition.INSTANCE.ensureNotNull(getDecorator(), "No decorator has been set",
                        IllegalStateException.class);
                initializeLayout(getLayout(), inflateTabsOnly);
            }

            @Override
            public void onWindowUnbound() {

            }

        };
    }

    /**
     * Notifies all listeners, that the tab switcher has been shown.
     */
    private void notifyOnSwitcherShown() {
        for (TabSwitcherListener listener : listeners) {
            listener.onSwitcherShown(this);
        }
    }

    /**
     * Notifies all listeners, that the tab switcher has been hidden.
     */
    private void notifyOnSwitcherHidden() {
        for (TabSwitcherListener listener : listeners) {
            listener.onSwitcherHidden(this);
        }
    }

    /**
     * Notifies all listeners, that the selected tab has been changed.
     *
     * @param selectedTabIndex The index of the currently selected tab as an {@link Integer} value or -1, if no tab
     *                         is currently selected
     * @param selectedTab      The currently selected tab as an instance of the class {@link Tab} or null,  if no
     *                         tab is currently selected
     */
    private void notifyOnSelectionChanged(final int selectedTabIndex,
                                          final Tab selectedTab) {
        for (TabSwitcherListener listener : listeners) {
            listener.onSelectionChanged(this, selectedTabIndex, selectedTab);
        }
    }

    /**
     * Notifies all listeners, that a specific tab has been added to the tab switcher.
     *
     * @param index     The index of the tab, which has been added, as an {@link Integer} value
     * @param tab       The tab, which has been added, as an instance of the class {@link Tab}. The tab may
     *                  not be null
     * @param animation The animation, which has been used to add the tab, as an instance of the class {@link
     *                  Animation}. The animation may not be null
     */
    private void notifyOnTabAdded(final int index, final Tab tab,
                                  final Animation animation) {
        for (TabSwitcherListener listener : listeners) {
            listener.onTabAdded(this, index, tab, animation);
        }
    }

    /**
     * Notifies all listeners, that a specific tab has been removed from the tab switcher.
     *
     * @param index     The index of the tab, which has been removed, as an {@link Integer} value
     * @param tab       The tab, which has been removed, as an instance of the class {@link Tab}. The tab may
     *                  not be null
     * @param animation The animation, which has been used to remove the tab, as an instance of the class
     *                  {@link Animation}. The animation may not be null
     */
    private void notifyOnTabRemoved(final int index, final Tab tab,
                                    final Animation animation) {
        for (TabSwitcherListener listener : listeners) {
            listener.onTabRemoved(this, index, tab, animation);
        }
    }

    /**
     * Notifies all listeners, that all tabs have been removed from the tab switcher.
     *
     * @param tabs      An array, which contains the tabs, which have been removed, as an array of the type
     *                  {@link Tab} or an empty array, if no tabs have been removed
     * @param animation The animation, which has been used to remove the tabs, as an instance of the class
     *                  {@link Animation}. The animation may not be null
     */
    private void notifyOnAllTabsRemoved(final Tab[] tabs,
                                        final Animation animation) {
        for (TabSwitcherListener listener : listeners) {
            listener.onAllTabsRemoved(this, tabs, animation);
        }
    }

    /**
     * Creates a new tab switcher, which allows to switch between multiple tabs.
     *
     * @param context The context, which should be used by the view, as an instance of the class {@link
     *                Context}. The context may not be null
     */
    public TabSwitcher(final Context context) {
        this(context, null);
    }

    /**
     * Creates a new tab switcher, which allows to switch between multiple tabs.
     *
     * @param context The context, which should be used by the view, as an instance of the class {@link
     *                Context}. The context may not be null
     * @param attrSet The attribute set, the view's attributes should be obtained from, as an instance of
     *                the type {@link AttrSet} or null, if no attributes should be obtained
     */
    public TabSwitcher(final Context context, final AttrSet attrSet) {
        this(context, attrSet, "");
    }

    /**
     * Creates a new tab switcher, which allows to switch between multiple tabs.
     *
     * @param context   The context, which should be used by the view, as an instance of the class {@link
     *                  Context}. The context may not be null
     * @param attrSet   The attribute set, the view's attributes should be obtained from, as an instance of
     *                  the type {@link AttrSet} or null, if no attributes should be obtained
     * @param styleName The default style to apply to this view. If 0, no style will be applied (beyond what
     *                  is included in the theme). This may either be an attribute resource, whose value will
     *                  be retrieved from the current theme, or an explicit style resource
     */
    public TabSwitcher(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        initialize(attrSet, styleName, 0);
    }

    /**
     * Setups the tab switcher to be associated with those menu items of its own toolbar menu, which
     * use a {@link TabSwitcherButton} as their action view. The icon of such menu items will
     * automatically be updated, when the number of tabs, which are contained by the tab switcher,
     * changes.
     * <p>
     * Calling this method is basically the same as calling <code>setupWithMenu(tabSwitcher,
     * tabSwitcher.getToolbarMenu(), listener)</code>. However, if the {@link Menu}, which is
     * returned by <code>tabSwitcher.getToolbarMenu()</code> is null, a {@link
     * ComponentTreeObserver.GlobalLayoutListener} is registered at the given tab switcher to setup the tab switcher as
     * soon as the menu is initialized.
     *
     * @param tabSwitcher The tab switcher, which should become associated with the menu items, as an instance
     *                    of the class {@link TabSwitcher}. The tab switcher may not be null
     * @param listener    The listener, which should be set to the menu items, which use a {@link
     *                    TabSwitcherButton} as their action view, as an instance of the type {@link
     *                    ClickedListener} or null, if no listener should be set
     */
    public static void setupWithToolbar(final TabSwitcher tabSwitcher, final ClickedListener listener) {
        Condition.INSTANCE.ensureNotNull(tabSwitcher, "The tab switcher may not be null");
        Toolbar[] toolbars = tabSwitcher.getToolbars();
        if (toolbars != null) {
            Toolbar toolbar = toolbars.length > 1 ? toolbars[TabSwitcher.SECONDARY_TOOLBAR_INDEX] :
                    toolbars[TabSwitcher.PRIMARY_TOOLBAR_INDEX];

            if (toolbar != null) {
                setupWithToolbar(tabSwitcher, toolbar, listener);
            } else {
                tabSwitcher.getComponentTreeObserver()
                        .addWindowBoundListener(new ComponentTreeObserver.WindowBoundListener() {

                            @Override
                            public void onWindowBound() {
//                                setupWithToolbar(tabSwitcher, toolbar, listener);
                            }

                            @Override
                            public void onWindowUnbound() {
                                ViewUtil.removeOnGlobalLayoutListener(tabSwitcher.getComponentTreeObserver(), this);
                            }

                        });
            }
        }
    }

    /**
     * Setups the tab switcher to be associated with those menu items of a specific menu, which use
     * a {@link TabSwitcherButton} as their action view. The icon of such menu items will
     * automatically be updated, when the number of tabs, which are contained by the tab switcher,
     * changes.
     *
     * @param tabSwitcher The tab switcher, which should become associated with the menu items, as an instance
     *                    of the class {@link TabSwitcher}. The tab switcher may not be null
     * @param toolbar     The menu, whose menu items should become associated with the given tab switcher, as
     *                    an instance of the type {@link Menu}. The menu may not be null
     * @param listener    The listener, which should be set to the menu items, which use a {@link
     *                    TabSwitcherButton} as their action view, as an instance of the type {@link
     *                    ClickedListener} or null, if no listener should be set
     */
    public static void setupWithToolbar(final TabSwitcher tabSwitcher,
                                        final Toolbar toolbar,
                                        final ClickedListener listener) {
        Condition.INSTANCE.ensureNotNull(tabSwitcher, "The tab switcher may not be null");
        Condition.INSTANCE.ensureNotNull(toolbar, "The menu may not be null");
        TabSwitcherButton tabSwitcherButton = toolbar.getActionView();
        toolbar.addMenuClickListener();
        tabSwitcherButton.setClickedListener(listener);
        tabSwitcherButton.setCount(tabSwitcher.getCount());
        toolbar.getMenu().setImageSrc(ResourceTable.Media_more_menu_white);
        tabSwitcherButton.setButtonColor(1, Color.WHITE);
        tabSwitcher.addListener(tabSwitcherButton);
    }

    public void startAnimation(AnimatorValue animatorValue) {
        if (animatorValue != null) {
            animatorValue.start();
        }
    }


    /**
     * Adds a listener, which should be notified about the tab switcher's events.
     *
     * @param listener The listener, which should be added, as an instance of the type {@link
     *                 TabSwitcherListener}. The listener may not be null
     */
    public final void addListener(final TabSwitcherListener listener) {
        Condition.INSTANCE.ensureNotNull(listener, "The listener may not be null");
        this.listeners.add(listener);
    }

    /**
     * Removes a specific listener, which should not be notified about the tab switcher's events,
     * anymore.
     *
     * @param listener The listener, which should be removed, as an instance of the type {@link
     *                 TabSwitcherListener}. The listener may not be null
     */
    public final void removeListener(final TabSwitcherListener listener) {
        Condition.INSTANCE.ensureNotNull(listener, "The listener may not be null");
        this.listeners.remove(listener);
    }

    /**
     * Returns the layout policy, which is used by the tab switcher.
     *
     * @return The layout policy, which is used by the tab switcher, as a value of the enum {@link
     * LayoutPolicy}. The layout policy may either be {@link LayoutPolicy#AUTO}, {@link
     * LayoutPolicy#PHONE} or {@link LayoutPolicy#TABLET}
     */

    public final LayoutPolicy getLayoutPolicy() {
        return layoutPolicy;
    }

    /**
     * Sets the layout policy, which should be used by the tab switcher.
     * <p>
     * Changing the layout policy after the view has been laid out does not have any effect.
     *
     * @param layoutPolicy The layout policy, which should be set, as a value of the enum {@link LayoutPolicy}.
     *                     The layout policy may either be {@link LayoutPolicy#AUTO}, {@link LayoutPolicy#PHONE}
     *                     or {@link LayoutPolicy#TABLET}
     */
    public final void setLayoutPolicy(final LayoutPolicy layoutPolicy) {
        Condition.INSTANCE.ensureNotNull(layoutPolicy, "The layout policy may not be null");

        if (this.layoutPolicy != layoutPolicy) {
            Layout previousLayout = getLayout();
            this.layoutPolicy = layoutPolicy;

            if (layout != null) {
                Layout newLayout = getLayout();

                if (previousLayout != newLayout) {
                    layout.detachLayout(false);
                    model.removeListener(layout);
                    touchEventDispatcher.removeEventHandler(layout.getDragHandler());
                    initializeLayout(newLayout, false);
                }
            }
        }
    }

    /**
     * Returns the layout of the tab switcher.
     *
     * @return The layout of the tab switcher as a value of the enum {@link Layout}. The layout may
     * either be {@link Layout#PHONE_PORTRAIT}, {@link Layout#PHONE_LANDSCAPE} or {@link
     * Layout#TABLET}
     */

    public final Layout getLayout() {
        return getOrientation(getContext()) == DisplayUtil.Orientation.LANDSCAPE ? Layout.PHONE_LANDSCAPE :
                Layout.PHONE_PORTRAIT;
    }

    /**
     * Adds a specific drag gesture to the tab switcher.
     *
     * @param dragGesture The drag gesture, which should be added, as an instance of the class {@link
     *                    DragGesture}. The drag gesture may not be null
     */
    public final void addDragGesture(final DragGesture dragGesture) {
        Condition.INSTANCE.ensureNotNull(dragGesture, "The drag gesture may not be null");
        AbstractTouchEventHandler eventHandler =
                new DragGestureEventHandlerFactory(this).fromGesture(dragGesture);
        touchEventDispatcher.addEventHandler(eventHandler);
    }

    /**
     * Removes a specific drag gesture from the tab switcher.
     *
     * @param dragGesture The drag gesture, which should be removed, as an instance of the class {@link
     *                    DragGesture}. The drag gesture may not be null
     */
    public final void removeDragGesture(final DragGesture dragGesture) {
        Condition.INSTANCE.ensureNotNull(dragGesture, "The drag gesture may not be null");
        AbstractTouchEventHandler eventHandler =
                new DragGestureEventHandlerFactory(this).fromGesture(dragGesture);
        touchEventDispatcher.removeEventHandler(eventHandler);
    }


    public boolean isStatePreserved() {
        return preserveState;
    }

    /**
     * Clears the saved state of a specific tab.
     *
     * @param tab The tab, whose saved state should be cleared, as an instance of the class {@link
     *            Tab}. The tab may not be null
     */
    public void clearSavedState(final Tab tab) {
        Condition.INSTANCE.ensureNotNull(tab, "The tab may not be null");
        ContentRecyclerAdapter contentRecyclerAdapter = model.getContentRecyclerAdapter();

        if (contentRecyclerAdapter != null) {
            contentRecyclerAdapter.clearSavedState(tab);
        }
    }

    /**
     * Clears the saved states of all tabs.
     */
    public void clearAllSavedStates() {
        ContentRecyclerAdapter contentRecyclerAdapter = model.getContentRecyclerAdapter();

        if (contentRecyclerAdapter != null) {
            contentRecyclerAdapter.clearAllSavedStates();
        }
    }

    /**
     * Notifies the tab switcher that a specific tab has changed. This will cause the content of the
     * tab to be updated by utilizing the tab switcher's adapter.
     *
     * @param tab The tab, which has changed, as an instance of the class {@link Tab}. The tab may not
     *            be null
     */
    public final void notifyTabChanged(final Tab tab) {
        Condition.INSTANCE.ensureNotNull(tab, "The tab may not be null");

        if (layout != null) {
            AbstractViewRecycler<Tab, Void> contentViewRecycler = layout.getContentViewRecycler();

            if (contentViewRecycler != null) {
                contentViewRecycler.notifyItemChanged(tab);
            }
        }
    }

    @Override
    public final void addTab(final Tab tab) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.addTab(tab);
            }

        });
    }

    @Override
    public final void addTab(final Tab tab, final int index) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.addTab(tab, index);
            }

        });
    }

    @Override
    public final void addTab(final Tab tab, final int index,
                             final Animation animation) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.addTab(tab, index, animation);
            }

        });
    }

    @Override
    public final void addAllTabs(final Collection<? extends Tab> tabs) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.addAllTabs(tabs);
            }

        });
    }

    @Override
    public final void addAllTabs(final Collection<? extends Tab> tabs, final int index) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.addAllTabs(tabs, index);
            }

        });
    }

    @Override
    public final void addAllTabs(final Collection<? extends Tab> tabs, final int index,
                                 final Animation animation) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.addAllTabs(tabs, index, animation);
            }

        });
    }

    @Override
    public final void addAllTabs(final Tab[] tabs) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.addAllTabs(tabs);
            }

        });
    }

    @Override
    public final void addAllTabs(final Tab[] tabs, final int index) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.addAllTabs(tabs, index);
            }

        });
    }

    @Override
    public final void addAllTabs(final Tab[] tabs, final int index,
                                 final Animation animation) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.addAllTabs(tabs, index, animation);
            }

        });
    }

    @Override
    public final void removeTab(final Tab tab) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.removeTab(tab);
            }

        });
    }

    @Override
    public final void removeTab(final Tab tab, final Animation animation) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.removeTab(tab, animation);
            }

        });
    }

    @Override
    public final void clear() {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.clear();
            }

        });
    }

    @Override
    public final void clear(final Animation animationType) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.clear(animationType);
            }

        });
    }

    @Override
    public final void selectTab(final Tab tab) {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.selectTab(tab);
            }

        });
    }

    @Override
    public final void selectTab(final int index) {
        model.selectTab(index);
    }


    @Override
    public final Tab getSelectedTab() {
        return model.getSelectedTab();
    }

    @Override
    public final int getSelectedTabIndex() {
        return model.getSelectedTabIndex();
    }

    @Override
    public final Iterator<Tab> iterator() {
        return model.iterator();
    }

    @Override
    public final boolean isEmpty() {
        return model.isEmpty();
    }

    @Override
    public final int getCount() {
        return model.getCount();
    }


    @Override
    public final Tab getTab(final int index) {
        return model.getTab(index);
    }

    @Override
    public final int indexOf(final Tab tab) {
        return model.indexOf(tab);
    }

    @Override
    public final boolean isSwitcherShown() {
        return model.isSwitcherShown();
    }

    @Override
    public final void showSwitcher() {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.showSwitcher();
            }

        });
    }

    @Override
    public final void hideSwitcher() {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                model.hideSwitcher();
            }

        });
    }

    @Override
    public final void toggleSwitcherVisibility() {
        enqueuePendingAction(new Runnable() {

            @Override
            public void run() {
                LogUtil.loge("=== enqueuePendingAction toggleSwitcherVisibility");
                model.toggleSwitcherVisibility();
            }

        });
    }

    @Override
    public final void setDecorator(final TabSwitcherDecorator decorator) {
        model.setDecorator(decorator);
    }

    @Override
    public final TabSwitcherDecorator getDecorator() {
        return model.getDecorator();
    }


    @Override
    public final LogLevel getLogLevel() {
        return model.getLogLevel();
    }

    @Override
    public final void setLogLevel(final LogLevel logLevel) {
        model.setLogLevel(logLevel);
    }

    @Override
    public final void setPadding(final int left, final int top, final int right, final int bottom) {
        // The model might be null on old API levels, where the super constructor implicitly invokes
        // the setPadding-method. We can simply ignore such method call and will set the correct
        // padding later on
        if (model != null) {
            model.setPadding(left, top, right, bottom);
        }
    }

    @Override
    public final int getPaddingLeft() {
        return model.getPaddingLeft();
    }

    @Override
    public final int getPaddingTop() {
        return model.getPaddingTop();
    }

    @Override
    public final int getPaddingRight() {
        return model.getPaddingRight();
    }

    @Override
    public final int getPaddingBottom() {
        return model.getPaddingBottom();
    }

    @Override
    public final int getPaddingStart() {
        return model.getPaddingStart();
    }

    @Override
    public final int getPaddingEnd() {
        return model.getPaddingEnd();
    }

    @Override
    public final void applyPaddingToTabs(final boolean applyPaddingToTabs) {
        model.applyPaddingToTabs(applyPaddingToTabs);
    }

    @Override
    public final boolean isPaddingAppliedToTabs() {
        return model.isPaddingAppliedToTabs();
    }


    @Override
    public final Element getTabIcon() {
        return model.getTabIcon();
    }

    @Override
    public final void setTabIcon(final int resourceId) {
        model.setTabIcon(resourceId);
    }

    @Override
    public final void setTabIcon(final PixelMap icon) {
        model.setTabIcon(icon);
    }

    @Override
    public final Color getTabIconTintList() {
        return model.getTabIconTintList();
    }

    @Override
    public final void setTabIconTint(final int color) {
        model.setTabIconTint(color);
    }

    @Override
    public final void setTabIconTintList(final Color tintList) {
        model.setTabIconTintList(tintList);
    }

    @Override
    public final Canvas.PorterDuffMode getTabIconTintMode() {
        return model.getTabIconTintMode();
    }

    @Override
    public final void setTabIconTintMode(final Canvas.PorterDuffMode mode) {
        model.setTabIconTintMode(mode);
    }


    @Override
    public final Color getTabBackgroundColor() {
        return model.getTabBackgroundColor();
    }

    @Override
    public final void setTabBackgroundColor(final int color) {
        model.setTabBackgroundColor(color);
    }

    @Override
    public final void setTabBackgroundColor(final Color colorStateList) {
        model.setTabBackgroundColor(colorStateList);
    }

    @Override
    public int getTabTitleBackgroundColor() {
        return model.getTabTitleBackgroundColor();
    }

    @Override
    public void setTabTitleBackgroundColor(int color) {

    }


    @Override
    public final int getTabContentBackgroundColor() {
        return model.getTabContentBackgroundColor();
    }

    @Override
    public final void setTabContentBackgroundColor(final int color) {
        model.setTabContentBackgroundColor(color);
    }


    @Override
    public final Color getTabTitleTextColor() {
        return model.getTabTitleTextColor();
    }

    @Override
    public final void setTabTitleTextColor(final int color) {
        model.setTabTitleTextColor(color);
    }

    @Override
    public final void setTabTitleTextColor(final Color colorStateList) {
        model.setTabTitleTextColor(colorStateList);
    }


    @Override
    public final Element getTabCloseButtonIcon() {
        return model.getTabCloseButtonIcon();
    }

    @Override
    public final int getTabProgressBarColor() {
        return model.getTabProgressBarColor();
    }

    @Override
    public final void setTabProgressBarColor(final int color) {
        model.setTabProgressBarColor(color);
    }

    @Override
    public final void setTabCloseButtonIcon(final int resourceId) {
        model.setTabCloseButtonIcon(resourceId);
    }

    @Override
    public final void setTabCloseButtonIcon(final PixelMap icon) {
        model.setTabCloseButtonIcon(icon);
    }

    @Override
    public final Color getTabCloseButtonIconTintList() {
        return model.getTabCloseButtonIconTintList();
    }

    @Override
    public final void setTabCloseButtonIconTint(final int color) {
        model.setTabCloseButtonIconTint(color);
    }

    @Override
    public final void setTabCloseButtonIconTintList(final Color tintList) {
        model.setTabCloseButtonIconTintList(tintList);
    }

    @Override
    public final Canvas.PorterDuffMode getTabCloseButtonIconTintMode() {
        return model.getTabCloseButtonIconTintMode();
    }

    @Override
    public final void setTabCloseButtonIconTintMode(final Canvas.PorterDuffMode mode) {
        model.setTabCloseButtonIconTintMode(mode);
    }

    @Override
    public final boolean isAddTabButtonShown() {
        return model.isAddTabButtonShown();
    }

    @Override
    public final void showAddTabButton(final AddTabButtonListener listener) {
        model.showAddTabButton(listener);
    }


    @Override
    public final Color getAddTabButtonColor() {
        return model.getAddTabButtonColor();
    }

    @Override
    public final void setAddTabButtonColor(final int color) {
        model.setAddTabButtonColor(color);
    }

    @Override
    public final void setAddTabButtonColor(final Color colorStateList) {
        model.setAddTabButtonColor(colorStateList);
    }

    @Override
    public final boolean areToolbarsShown() {
        return model.areToolbarsShown();
    }

    @Override
    public final void showToolbars(final boolean show) {
        model.showToolbars(show);
    }


    @Override
    public final CharSequence getToolbarTitle() {
        Toolbar[] toolbars = getToolbars();
        return toolbars != null ? toolbars[PRIMARY_TOOLBAR_INDEX].getTitle() :
                model.getToolbarTitle();
    }

    @Override
    public final void setToolbarTitle(final int resourceId) {
        model.setToolbarTitle(resourceId);
    }

    @Override
    public final void setToolbarTitle(final CharSequence title) {
        model.setToolbarTitle(title);
    }


    @Override
    public final Element getToolbarNavigationIcon() {
        Toolbar[] toolbars = getToolbars();
        return toolbars != null ? toolbars[PRIMARY_TOOLBAR_INDEX].getNavigationIcon() :
                model.getToolbarNavigationIcon();
    }

    @Override
    public final void setToolbarNavigationIcon(final Element icon,
                                               final ClickedListener listener) {
        model.setToolbarNavigationIcon(icon, listener);
    }

    @Override
    public final void setToolbarNavigationIcon(final int resourceId,
                                               final ClickedListener listener) {
        model.setToolbarNavigationIcon(resourceId, listener);
    }

    @Override
    public final Color getToolbarNavigationIconTintList() {
        return model.getToolbarNavigationIconTintList();
    }

    @Override
    public final void setToolbarNavigationIconTint(final int color) {
        model.setToolbarNavigationIconTint(color);
    }

    @Override
    public final void setToolbarNavigationIconTintList(final Color tintList) {
        model.setToolbarNavigationIconTintList(tintList);
    }

    @Override
    public final Canvas.PorterDuffMode getToolbarNavigationIconTintMode() {
        return model.getToolbarNavigationIconTintMode();
    }

    @Override
    public final void setToolbarNavigationIconTintMode(final Canvas.PorterDuffMode mode) {
        model.setToolbarNavigationIconTintMode(mode);
    }

    @Override
    public final void inflateToolbarMenu(final int resourceId,
                                         final OnMenuItemClickListener listener) {
        model.inflateToolbarMenu(resourceId, listener);
    }

    @Override
    public final long getTabPreviewFadeThreshold() {
        return model.getTabPreviewFadeThreshold();
    }

    @Override
    public final void setTabPreviewFadeThreshold(final long threshold) {
        model.setTabPreviewFadeThreshold(threshold);
    }

    @Override
    public final long getTabPreviewFadeDuration() {
        return model.getTabPreviewFadeDuration();
    }

    @Override
    public final void setTabPreviewFadeDuration(final long duration) {
        model.setTabPreviewFadeDuration(duration);
    }


    @Override
    public final Component getEmptyView() {
        return model.getEmptyView();
    }

    @Override
    public void setEmptyView(final Component view) {
        model.setEmptyView(view);
    }

    @Override
    public final void setEmptyView(final Component view, final long animationDuration) {
        model.setEmptyView(view, animationDuration);
    }

    @Override
    public final void setEmptyView(final int resourceId) {
        model.setEmptyView(resourceId);
    }

    @Override
    public final void setEmptyView(final int resourceId, final long animationDuration) {
        model.setEmptyView(resourceId);
    }

    @Override
    public final boolean areSavedStatesClearedWhenRemovingTabs() {
        return model.areSavedStatesClearedWhenRemovingTabs();
    }

    @Override
    public final void clearSavedStatesWhenRemovingTabs(final boolean clear) {
        model.clearSavedStatesWhenRemovingTabs(clear);
    }

    @Override
    public final void addCloseTabListener(final TabCloseListener listener) {
        model.addCloseTabListener(listener);
    }

    @Override
    public final void removeCloseTabListener(final TabCloseListener listener) {
        model.removeCloseTabListener(listener);
    }

    @Override
    public final void addTabPreviewListener(final TabPreviewListener listener) {
        model.addTabPreviewListener(listener);
    }

    @Override
    public final void removeTabPreviewListener(final TabPreviewListener listener) {
        model.removeTabPreviewListener(listener);
    }

    @Override
    public final boolean isAnimationRunning() {
        return layout != null && layout.isAnimationRunning();
    }


    @Override
    public final ComponentContainer getTabContainer() {
        return layout != null ? layout.getTabContainer() : null;
    }

    @Override
    public final Toolbar[] getToolbars() {
        return layout != null ? layout.getToolbars() : null;
    }


    @Override
    public final Menu getToolbarMenu() {
        return layout != null ? layout.getToolbarMenu() : null;
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent event) {
        return touchEventDispatcher.handleTouchEvent(event);
    }

    /*@Override
    public final Sequenceable onSaveInstanceState() {
        Sequenceable superState = super.onSaveInstanceState();

        if (layout != null && preserveState) {
            TabSwitcherState savedState = new TabSwitcherState(superState);
            savedState.layoutPolicy = layoutPolicy;
            savedState.modelState = new Bundle();
            Pair<Integer, Float> pair = layout.detachLayout(true);

            if (pair != null) {
                savedState.modelState
                        .putInt(TabSwitcherModel.REFERENCE_TAB_INDEX_EXTRA, pair.first);
                savedState.modelState
                        .putFloat(TabSwitcherModel.REFERENCE_TAB_POSITION_EXTRA, pair.second);
                model.setReferenceTabIndex(pair.first);
                model.setReferenceTabPosition(pair.second);
            } else {
                model.setReferenceTabPosition(-1);
                model.setReferenceTabIndex(-1);
            }

            model.removeListener(layout);
            layout = null;
            executePendingAction();
            getComponentTreeObserver().addTreeLayoutChangedListener(
                    new AbstractTabSwitcherLayout.LayoutListenerWrapper(this, createGlobalLayoutListener(true)));
            model.saveInstanceState(savedState.modelState);
            return savedState;
        }

        return superState;
    }

    @Override
    public final void onRestoreInstanceState(final Sequenceable state) {
        if (state instanceof TabSwitcherState && preserveState) {
            TabSwitcherState savedState = (TabSwitcherState) state;
            this.layoutPolicy = savedState.layoutPolicy;
            model.restoreInstanceState(savedState.modelState);
            super.onRestoreInstanceState(savedState.getSuperState());
        } else {
            super.onRestoreInstanceState(state);
        }
    }*/
}
